Skip to content

ts(B-0086): port 2 hygiene scripts (.sh→.ts) — slice 10 of TS/Bun migration#883

Merged
AceHack merged 3 commits intomainfrom
lane-b/ts-bun-slice-10-counterweight-audit-2026-04-30
Apr 30, 2026
Merged

ts(B-0086): port 2 hygiene scripts (.sh→.ts) — slice 10 of TS/Bun migration#883
AceHack merged 3 commits intomainfrom
lane-b/ts-bun-slice-10-counterweight-audit-2026-04-30

Conversation

@AceHack
Copy link
Copy Markdown
Member

@AceHack AceHack commented Apr 30, 2026

Summary

  • Slice 10 of the TS+Bun migration. Ports 2 hygiene scripts byte-equivalent against bash.
  • Counterweight-cluster opened (Otto-278 cadenced re-read) + first write-side script ported (append-tick-history-row).

Files

  • `tools/hygiene/counterweight-audit.{sh→ts}` — Otto-278 cadenced counterweight-memory audit (forces re-read on a cadence)
  • `tools/hygiene/append-tick-history-row.{sh→ts}` — chronological-tail-append validator for tick-history rows

Plus:

Equivalence

script result
counterweight-audit byte-equivalent on default + all explicit cadence levels
append-tick-history-row byte-equivalent on usage error + malformed-row + out-of-order-timestamp paths modulo script self-reference (.sh vs .ts)

Lint + types

Both files pass:

  • `bun x tsc --noEmit` clean
  • `bun x eslint` clean (typescript-eslint strictTypeChecked + stylisticTypeChecked + sonarjs)

Test plan

  • CI: lint + typecheck on the slice files
  • CI: `bun tools/hygiene/counterweight-audit.ts` produces parseable markdown
  • CI: `bun tools/hygiene/append-tick-history-row.ts` rejects out-of-order timestamps

🤖 Generated with Claude Code

AceHack added 3 commits April 30, 2026 00:02
First script of slice 10. Cadenced counterweight-memory audit
(Otto-278) — emits per-counterweight audit questions for
`memory/*otto_*.md` files in mtime-newest-first order.

Byte-equivalent against bash original under default cadence
(quick, count=3). Diff against current repo state shows zero
divergence.

Mechanical changes:
- bash glob `memory/*otto_*.md` → readdirSync filter on
  `includes('otto_') && endsWith('.md')`
- bash BSD/GNU stat probe (stat -f %m vs stat -c %Y) replaced
  with statSync().mtimeMs (single platform-agnostic Node API)
- bash YAML frontmatter awk parser → manual fence-aware char
  walk in extractNameField (avoids sonarjs slow-regex flag on
  the trailing `(.*)$` capture)
- bash arg parser with case statement → classifyArg helper +
  ArgStep tagged union to keep parseArgs cognitive-complexity
  under sonarjs threshold (15)
- bash `mktemp` + sort-rn pipeline replaced with in-memory
  array sort (faster, no temp file)
- All output strings preserved verbatim including 'newest first'
  language, em-dashes, audit-question wording

Lint-clean (eslint + sonarjs + tsc).
- slice-audits.md: append slice-10 audit (2 ports — counterweight-audit
  + append-tick-history-row, first write-side port).
- RESUME.md: bump slice-8 → slice-9-merged (#882, commit 02266a7).
  Milestone 28 → 31. Bucket B 16 → 14.
Copilot AI review requested due to automatic review settings April 30, 2026 04:04
@AceHack AceHack enabled auto-merge (squash) April 30, 2026 04:04
@AceHack AceHack merged commit 271bc38 into main Apr 30, 2026
26 checks passed
@AceHack AceHack deleted the lane-b/ts-bun-slice-10-counterweight-audit-2026-04-30 branch April 30, 2026 04:07
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Ports two repo hygiene scripts from bash to TypeScript/Bun as part of the ongoing TS/Bun migration, and records the slice-10 audit/trajectory status in docs.

Changes:

  • Add tools/hygiene/counterweight-audit.ts (cadenced counterweight-memory audit output generator).
  • Add tools/hygiene/append-tick-history-row.ts (tick-history row validator + append helper).
  • Update TS/Bun migration trajectory docs for slice-10 status/audit notes.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
tools/hygiene/counterweight-audit.ts New TS/Bun implementation of the counterweight audit report generator.
tools/hygiene/append-tick-history-row.ts New TS/Bun implementation of the tick-history append validator/appender.
docs/trajectories/typescript-bun-migration/slice-audits.md Appends slice-10 audit notes and equivalence summary.
docs/trajectories/typescript-bun-migration/RESUME.md Updates milestone counts/status and slice-10 next action notes.

#!/usr/bin/env bun
// append-tick-history-row.ts — appends a row to the loop-tick-history
// using simple append (avoids the Edit-tool's reverse-chronological
// bug shape Aaron flagged 2026-04-26).
Comment on lines +16 to +20
// Exit codes:
// 0 appended successfully
// 1 row malformed OR timestamp out of order
// 2 wrong number of arguments

process.stdout.write("the file and read the rule body + maintainer quote, then\n");
process.stdout.write("answer the per-counterweight audit questions below.\n");
process.stdout.write("\n");
process.stdout.write("_Tool: `tools/hygiene/counterweight-audit.sh` (Otto-278\n");
Comment on lines +18 to +26
// Exit codes:
// 0 normal completion
// 2 usage error (unknown flag, missing value, invalid cadence, etc.)

import { readdirSync, readFileSync, statSync } from "node:fs";
import { join } from "node:path";
import { spawnSync } from "node:child_process";

type ExitCode = 0 | 2;
**Status**: Active (Lane B slice 8 merged — [#880](https://github.com/Lucent-Financial-Group/Zeta/pull/880), commit `988de70`)
**Milestone**: 28 hygiene/lint/audit scripts ported (2 from #849 + 3 from #866 + 3 from #868 + 3 from #870 + 2 from #872 + 3 from #874 + 3 from #876 + 3 from #878 + 3 from #880 + 3 in-flight in slice-9). **Cluster H complete (5/5)** in #878 + #880; slice-9 opens **agency-signature-pair cluster** (validate-agencysignature-pr-body + audit-agencysignature-main-tip — paired ferry-7 enforcement-instrument set per Amara) + capture-tick-snapshot (snapshot-pinning per Amara 4th-ferry). 16 Bucket B files remain.
**Status**: Active (Lane B slice 9 merged — [#882](https://github.com/Lucent-Financial-Group/Zeta/pull/882), commit `02266a7`)
**Milestone**: 31 hygiene/lint/audit scripts ported (2 from #849 + 3 from #866 + 3 from #868 + 3 from #870 + 2 from #872 + 3 from #874 + 3 from #876 + 3 from #878 + 3 from #880 + 3 from #882 + 2 in-flight in slice-10). **Cluster H complete** + agency-signature-pair complete; slice-10 opens **counterweight-cluster + write-side-tools** (counterweight-audit + append-tick-history-row). 14 Bucket B files remain.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 460ee5d743

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

encoding: "utf8",
maxBuffer: SPAWN_MAX_BUFFER,
});
if (result.status !== 0) return process.cwd();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve git-root failure behavior before appending history

The TypeScript port falls back to process.cwd() when git rev-parse --show-toplevel fails, but the shell script exited immediately on that failure (set -e with REPO_ROOT="$(git ...)"). This changes a hard-fail into a potentially mutating path: if the command is run outside a repo (or without git) in a directory that happens to contain docs/hygiene-history/loop-tick-history.md, it can append to the wrong file tree instead of stopping.

Useful? React with 👍 / 👎.

AceHack added a commit that referenced this pull request Apr 30, 2026
- slice-audits.md: append slice-11 audit (2 ports — backfill_dv2_frontmatter
  + audit-packages).
- RESUME.md: bump slice-9 → slice-10-merged (#883, commit 271bc38).
  Milestone 31 → 33. Bucket B 14 → 12.
AceHack added a commit that referenced this pull request Apr 30, 2026
…884)

* ts(slice-11, wip 1/N): port backfill_dv2_frontmatter (.sh→.ts)

First script of slice 11. Mechanical DV-2.0 frontmatter backfill
for SKILL.md files. Computes 5 missing fields from git history
(record_source / load_datetime / last_updated / status /
bp_rules_cited) and injects before closing frontmatter fence.

Byte-equivalent against bash original on --dry-run mode.

Mechanical changes:
- bash awk frontmatter parse → fieldPresent + dashCount helpers
- bash compute_record_source heuristic (round-N regex + author-
  date fallback) preserved as computeRecordSource
- bash mktemp + awk inject + mv rename → readFileSync +
  injectBeforeSecondFence + writeFileSync
- bash 'INJECT_BLOB env-passing' awk pattern → in-memory string
  array
- bash --all glob (find -maxdepth 2) → readdirSync filter
- All output strings preserved verbatim

Lint-clean (eslint + sonarjs + tsc).

* ts(slice-11, wip 2/N): port audit-packages (.sh→.ts)

Second script of slice 11. Network-dependent NuGet feed audit:
checks every Directory.Packages.props PackageVersion entry
against `dotnet package search --exact-match`.

Mechanical changes:
- bash grep+sed extraction → PACKAGE_RE.exec loop yielding
  PackageEntry tuples
- bash awk pipe-table parse → cols.split('|').map(trim) + col2
  match check; preserves 'last matching row' semantics
- bash printf '%-35s %-15s ...' → pad helper
- 3 statuses preserved: ✓ up-to-date / ? couldn't query /
  ⚠ bump available
- Exit-code semantics preserved: 0 = all queryable on latest,
  1 = one or more bumps available

Network-dependent so byte-equivalence requires offline-mode
testing (both bash + TS produce '?' for all packages when
`dotnet package search` fails — verified locally).

Lint-clean (eslint + sonarjs + tsc).

* trajectory(ts-bun): slice 11 audit substrate + RESUME tracker

- slice-audits.md: append slice-11 audit (2 ports — backfill_dv2_frontmatter
  + audit-packages).
- RESUME.md: bump slice-9 → slice-10-merged (#883, commit 271bc38).
  Milestone 31 → 33. Bucket B 14 → 12.

* review(slice-11): address PR #884 CodeQL TOCTOU findings

CodeQL flagged 2 file-system-race-condition findings on
backfill_dv2_frontmatter.ts where stat-then-read patterns
(statSync(path).isFile() followed by later readFileSync) could
race. Refactored to:

1. processOne: removed initial statSync gate; readFileSync
   directly with try/catch + ENOENT/EISDIR detection on the
   error.code property to map to warn/error outcomes. Preserves
   the original bash 'skip non-file' UX without the race window.
2. findAllSkillFiles: removed statSync probe of candidate paths;
   readdirSync only filters by directory entries; processOne
   handles missing/non-file paths gracefully via its try/catch.

Equivalence preserved: --dry-run output unchanged on a fresh
SKILL.md fixture.

Lint-clean (eslint + sonarjs + tsc).

* review(slice-11): restore atomic rewrite per Codex P2

Codex flagged backfill_dv2_frontmatter.ts losing the bash
original's atomic-rewrite invariant: bash used `mktemp` + `mv`
so a crash/kill/disk-full mid-write leaves the source file
intact. The TS port's writeFileSync truncates in place — a
real regression vs bash.

Restored: write to sibling tmp file (`<path>.tmp.<pid>.<ts>`)
then renameSync to target. Same-filesystem rename is atomic
on POSIX. Cleanup on failure: unlink the tmp + return structured
error outcome (exitCode 2).

* review(slice-11): address PR #884 round-3 threads

Three real fixes:
- backfill_dv2_frontmatter: chdir to repoRoot() before --all scan
  (was cwd-dependent; both bash + TS had the bug, TS fixes at the
  entry point) + restored full canonical script path in usage
  message to match emitHelp()
- slice-audits.md: corrected audit-packages.ts line count
  '51 → 154' to '51 → 143' (actual wc -l)

Three pushed back as intentional bash-equivalence:
- audit-packages stdout vs stderr — bash uses echo to stdout
- audit-packages exit-code 64 — bash uses 1
- backfill gitOutput silently returns empty — bash awk on empty
  input behaves identically (both produce empty injected values)

Atomic-rewrite duplicate finding (already addressed in 5baf773)
resolved as duplicate.

* review(slice-11): audit-packages repoRoot via import.meta.url per Codex P2

Codex flagged that the audit-packages.ts repoRoot using
`git rev-parse --show-toplevel` with cwd fallback regresses
invocation semantics from the bash original
(`cd "$(dirname "$0")/.." && pwd`). Bash resolves the script
location and walks up; TS+git-rev-parse fails outside a git
checkout (falls back to wrong cwd).

Fixed: switch to `fileURLToPath(import.meta.url)` + `resolve`
to match bash's script-relative resolution. Now works from any
caller directory and outside git checkouts.

* review(slice-11): defensive rename fallback per Codex P0

Codex flagged renameSync(tmpPath, file) as a potential Windows
data-loss issue (older Node fs.rename refused to overwrite on
Windows). Modern Node/Bun renameSync DOES overwrite atomically
on all platforms, but edge cases (Windows file locks from open
editors, permission-restricted network shares) can still fail.

Added defense-in-depth fallback: catch the renameSync failure,
unlink the destination, then retry rename. Loses atomicity for
the failure window but recovers correctness. Bash 'mv' has the
same fragility — same UX as bash, just more robust.

* review(slice-11): preserve-original-on-failure + fail-on-empty-parse per Codex P1+P2

P1 backfill_dv2_frontmatter (preserve-original-on-failure):
Removed the unlink-before-retry fallback that risked deleting the
original SKILL.md without successfully writing the new content.
On rename failure, return error with tmp-preserved message; do
NOT touch the original. Modern Node/Bun renameSync handles the
common case atomically; rare failures (Windows file lock, perms,
disk-full) leave the original intact + tmp recoverable.

P2 audit-packages (fail-on-empty-parse):
If PACKAGE_RE.exec yields 0 entries on a non-empty
Directory.Packages.props, the regex has likely drifted from the
file format. Silent success hides this. Now returns exit 1 with
clear error pointing at the regex-drift suspicion.

* review(slice-11): fix RESUME milestone math per Copilot

Headline said '33 hygiene/lint/audit scripts ported' but the
parenthesized addends summed to 32 (including the 2 in-flight).
Two issues conflated:

1. 'ported' implies merged but the count included in-flight.
2. Off-by-one — 11 merged-PR contributions sum to 30, not 31.

Fixed: '30 ported + 2 in-flight = 32 total' with explicit
delineation between merged and in-flight.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants